//
//  ProcessMonitor.m
//  ProcessTimeBudget
//
//  Created by isempty.chiu on 2010/9/13.
//  Copyright 2010 LifeMotion. All rights reserved.
//

#import "ProcessMonitor.h"

@implementation ProcessMonitor
BOOL unitTestRunning_ = NO;
@synthesize ActiveProcessName = activeProcessName_;
@synthesize ActiveStartTime = activeStartTime_;

+(BOOL) isRunningUnitTest
{
	return unitTestRunning_;
}

+(ProcessMonitor*) sharedProcMonitor
{
	static ProcessMonitor* processMonitor = nil;
	if(processMonitor == nil)
		processMonitor = [[ProcessMonitor alloc] init];
	
	return processMonitor;
}

-(id)init
{	
	self = [super init];	
	if(self)
	{
		activeProcessName_ = nil;
		activeDuration_ = -1;
		activeStartTime_ = nil;
		
		timer_ = nil;
		runningTime_ = 0;
	}

	return self;
}

-(void) dealloc
{
	if(timer_)
		[timer_ invalidate];
	
	if(activeProcessName_)
		[activeProcessName_ release];
	if(activeStartTime_)
		[activeStartTime_ release];
	
	[super dealloc];
}

- (NSInteger) RunningTime
{
	return runningTime_ / 2;
}

- (NSTimeInterval) ActiveDuration
{
	NSTimeInterval counter = activeDuration_ / 2;
	if(!unitTestRunning_)
	{
		NSDate *now = [NSDate date];
		NSTimeInterval interval = [now timeIntervalSinceDate:activeStartTime_];
		if(interval < 0 || ((interval - counter) / counter * 100.0) > 10.0)
			return counter;	//error handle
		else
			return interval;
	}
	else
		return counter;
	
}

- (NSString*) getActiveWindow
{
	@synchronized(self)
	{
		NSDictionary *activeAppDict = [[NSWorkspace sharedWorkspace] activeApplication];	
		if (![[activeAppDict objectForKey:@"NSApplicationName"] isEqualToString:activeProcessName_])
		{
			return [NSString stringWithFormat:@"%@", [activeAppDict objectForKey:@"NSApplicationName"]];
		}
	}
	return nil; 
}

- (void) doChangeNotify
{
	NSString* currentAppName = [[NSRunningApplication currentApplication] localizedName];
	if(![activeProcessName_ isEqualToString:currentAppName])
	{
		NSLog(@"ActiveApplication = %@ Duration = %.1f sec", activeProcessName_, activeDuration_/2);
		[[NSNotificationCenter defaultCenter] postNotificationName:@"ActiveChanged" object:self];
	}	
}

- (void) activeAppChanged:(NSString*)appName
{
	@synchronized(self)
	{	
		if(activeDuration_ >= 10)
		{
			[self doChangeNotify];
		}
		
		if(activeProcessName_ != nil)
		{
			[activeProcessName_ release];	
		}			
		if(activeStartTime_ != nil)
		{
			[activeStartTime_ release];
		}
		activeDuration_ = 0;
		
		activeProcessName_ = [[NSString alloc] initWithString:appName];
		activeStartTime_ = [NSDate date];
		[activeStartTime_ retain];
	}
}

- (void) handleTimer: (NSTimer *)theTimer
{
	//get active application
	NSString* applicationName = (NSString*)[self getActiveWindow];
	if(applicationName != nil)
	{
		[self activeAppChanged:applicationName];
	}
	else {
		activeDuration_ = activeDuration_ + 1;
	}
	
	NSString* currentAppName = [[NSRunningApplication currentApplication] localizedName];
	if(![activeProcessName_ isEqualToString:currentAppName])
	{
		[self willChangeValueForKey:@"RunningTime"];
		runningTime_ = runningTime_ + 1;
		[self didChangeValueForKey:@"RunningTime"];
	}
}

- (void) enableTimer: (BOOL) bEnable
{
	if(bEnable)
	{
		timer_ = [NSTimer scheduledTimerWithTimeInterval: 0.5
												  target: self
												selector: @selector(handleTimer:)
												userInfo: nil
												 repeats: YES];
	}
	else {
		[timer_ invalidate];
		timer_ = nil;
	}	
}

- (void) forceActiveChanged
{
	if(timer_ == nil)
		return;
	[self enableTimer:NO];
	if(activeDuration_ < 10)
		[self doChangeNotify];
	[self activeAppChanged:[NSString stringWithFormat:@"%@", activeProcessName_]]; 
	[self enableTimer:YES];	
}

//UnitTest Begin
#import <stdlib.h>
#import <time.h>

- (NSInteger) getRandomInteger
{
	return (NSInteger)random() % 25000;
}

- (void) runUnitTest
{
	//initialize
	[self activeAppChanged:@"TestApp1"];
	runningTime_ = 91234;
	
	//stress unit test
	srandom(time(NULL));
	for(int i = 0; i < 3000; i++)
	{
		activeDuration_ = 15 + [self getRandomInteger];
		[self activeAppChanged:[NSString stringWithFormat:@"UnitTest-%d", i]];
	}
	
	//memory leak test
	
	//other test
	
	//end test
}
//UnitTest End

- (void) runMonitor:(BOOL)bStart
{
	if(unitTestRunning_)
		[self runUnitTest];
	else {
		if(bStart && timer_ == nil)
		{
			[self enableTimer:YES];
		}
		else if(!bStart && timer_)
		{
			[self enableTimer:NO];
		}
	}
}
@end
